using System.Globalization;
using System.Threading;
using Bots.BGBuddy.Helpers;
using CommonBehaviors.Actions;
using Styx.Combat.CombatRoutine;
using Styx.Logic;
using Styx.Logic.Combat;
using Styx.Logic.Inventory;
using Styx.Logic.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using System;
using System.Linq;
using Natfoth.Lists;
using System.Collections.Generic;
using Styx;
using Styx.Helpers;
using Styx.WoWInternals.World;
using TreeSharp;
using Action = TreeSharp.Action;

namespace Natfoth.Helpers
{
    internal static class Lib
    {
        private static LocalPlayer Me { get { return ObjectManager.Me; } }
        private static readonly List<WoWPetSpell> PetSpells = new List<WoWPetSpell>();
        private static WoWUnit Targ { get { return Me.CurrentTarget; } }

        public delegate WoWUnit UnitSelectionDelegate(object context);

        public delegate bool SimpleBooleanDelegate(object context);

        public static int HealingSpellTimer { get; set; }

        public static int Talent(int tree, int talent)
        {
            List<string> a = Lua.GetReturnValues("return GetTalentInfo(" + tree.ToString(CultureInfo.InvariantCulture) + "," + talent.ToString(CultureInfo.InvariantCulture) + ",false,false,nil)");

            int num = Convert.ToInt32(a[4]);

            return num;
        }

        public static RunStatus FaceTarget()
        {
            Targ.Face();
            return RunStatus.Success;
        }

        #region Navigate Path
        public class NeedToNavigatePath : Decorator
        {
            public NeedToNavigatePath(Composite child) : base(child) { }

            protected override bool CanRun(object context)
            {
                if (!Me.GotTarget) return false;
                if (GetLazyRaiderSettings().Contains("always")) return false;

                return !Navigator.CanNavigateFully(Me.Location, Targ.Location, 20);
            }
        }

        public class NavigatePath : Action
        {
            protected override RunStatus Run(object context)
            {
                Utils.Log("Can not navigate to target's location. Blacklisting", Utils.Colour("Red"));
                Target.BlackList(Utils.IsBattleground ? 10 : 30);

                return RunStatus.Success;
            }
        }
        #endregion

        #region Blacklist Pull Target
        public class NeedToBlacklistPullTarget : Decorator
        {
            public NeedToBlacklistPullTarget(Composite child) : base(child) { }

            protected override bool CanRun(object context)
            {
                if (GetLazyRaiderSettings().Contains("always")) return false;
                return !Target.IsValidPullTarget;
            }
        }

        public class BlacklistPullTarget : Action
        {
            protected override RunStatus Run(object context)
            {
                Utils.Log(string.Format("Bad pull target blacklisting and finding another target."), Utils.Colour("Red"));
                Target.BlackList(100);
                Me.ClearTarget();

                return RunStatus.Success;
            }
        }
        #endregion

        #region Pull Timer / Timeout
        public class NeedToCheckPullTimer : Decorator
        {
            public NeedToCheckPullTimer(Composite child) : base(child) { }

            protected override bool CanRun(object context)
            {
                if (GetLazyRaiderSettings().Contains("always")) return false;

                return Target.PullTimerExpired;
            }
        }

        public class CheckPullTimer : Action
        {
            protected override RunStatus Run(object context)
            {
                Utils.Log(string.Format("Unable to pull {0}, blacklisting and finding another target.", Me.CurrentTarget.Name), System.Drawing.Color.FromName("Red"));
                Target.BlackList(120);
                Me.ClearTarget();

                return RunStatus.Success;
            }
        }
        #endregion


        public static List<WoWUnit> Adds
        {
            get
            {
                return ObjectManager.GetObjectsOfType<WoWUnit>(false, false).Where(p => p.IsHostile && !p.Dead && !p.IsPet && p.DistanceSqr <= 40 * 40).ToList();
            }
        }

        public static List<string> Clua(string cmd)
        {
            return Lua.GetReturnValues(cmd, "abc.lua");
        }

        public static void LogBlueMessage(string message)
        {
            Utils.Log("[AkatoshCC] " + message, Utils.Colour("Aqua"));
        }

        public static void LogGreenMessage(string message)
        {
            Utils.Log("[AkatoshCC] " + message, Utils.Colour("Aquamarine"));
        }


        /*-------------------------
        --------Unit Type Check----
        -------------------------*/

        #region Priest
        public static bool PriestDedicatedHealerCheck()
        {
            return PriestSettings.PartyHealWhen.Contains("dedicated healer") && (ObjectManager.Me.IsInInstance || Utils.IsBattleground) && PriestSettings.PartyHealWhenSpec.Contains(ClassHelpers.Priest.ClassSpec.ToString());
        }

        #endregion


        //Target is a raid boss
        public static bool IsRaidBoss()
        {
            List<string> classcheck = Lua.GetReturnValues("return UnitClassification(\"target\")", "abc.lua");
            if (classcheck[0] == "worldboss")
            {
                return true;
            }
            return false;
        }

        //Is any boss level mob
        public static bool IsGenericBoss()
        {
            return BossList.BossIds.Contains(Me.CurrentTarget.Entry);
        }

        //Is Undead of Demon
        public static bool IsUdDemon()
        {
            return Me.CurrentTarget.CreatureType == WoWCreatureType.Undead
                    || Me.CurrentTarget.CreatureType == WoWCreatureType.Demon;
        }



        /*-------------------------
        --------Buff Crap----------
        -------------------------*/
        //Has Buff
        public static bool HasBuff(string buffName)
        {
            List<string> b = Lua.GetReturnValues("return UnitBuff(\"player\",\"" + buffName + "\")", "abc.lua");
            if (Equals(null, b))
                return false;

            return true;
        }
        //Stacks of buff
        public static int BuffSide(string buffName)
        {
            List<string> b = Lua.GetReturnValues("return UnitBuff(\"player\",\"" + buffName + "\")", "abc.lua");
            if (Equals(null, b))
            {
                return 0;
            }

            return Convert.ToInt32(b[3]);
        }
        //Buff Time Remaining
        public static double BuffTimeRemaining(string buffName)
        {
            List<string> a = Lua.GetReturnValues("return GetTime()", "abc.lua");
            List<string> b = Lua.GetReturnValues("return UnitBuff(\"player\",\"" + buffName + "\")", "abc.lua");
            if (Equals(null, b))
            {
                return 0;
            }

            return Convert.ToDouble(b[6]) - Convert.ToDouble(a[0]);
        }
        //Heroism
        public static bool Hero()
        {
            return Me.HasAnyAura("Heroism", "Time Warp", "Ancient Hysteria", "Bloodlust");
        }

        public static bool WeaponEnchant()
        {

            List<string> enchants = Clua("return GetWeaponEnchantInfo()");

            if (enchants[0] == "" || enchants[0] == "nil")
            {
                return true;
            }

            if ((enchants[3] == "" || enchants[3] == "nil") && !Equals(null,Clua("return OffhandHasWeapon()")))
            {
                return true;
            }

            return false;
        }

        public static bool NeedsTotems()
        {
            List<string> totems = Clua("return GetTotemInfo(1)");

            if (Equals(totems, null))
            {
                return true;
            }

            if (totems[1] != "Searing Totem")
            {
                return true;
            }

            return false;
        }


        /*-------------------------
        --------Debuff Checks------
        -------------------------*/

        #region Combat Timer / Timeout
        public class NeedToCheckCombatTimer : Decorator
        {
            public NeedToCheckCombatTimer(Composite child) : base(child) { }

            protected override bool CanRun(object context)
            {
                if (Utils.IsBattleground) return false;
                if (!Me.GotTarget || Me.CurrentTarget.Dead) return false;
                if (Target.IsElite) return false;
                if (Targ.Name.Contains("Training Dummy")) return false;
                if (Me.IsInInstance) return false;

                return Target.CombatTimerExpired && Target.IsHealthPercentAbove(98);
            }
        }

        public class CheckCombatTimer : Action
        {
            protected override RunStatus Run(object context)
            {
                Utils.Log(string.Format("Combat with {0} is bugged, blacklisting and finding another target.", Me.CurrentTarget.Name), System.Drawing.Color.FromName("Red"));
                Target.BlackList(60);
                Utils.LagSleep();

                return RunStatus.Success;
            }
        }
        #endregion


        //Has Debuff Generic
        public static bool UnitDebuff(string buffName)
        {
            return false;

             /*List<string> b = Lua.GetReturnValues("return UnitDebuff(\"target\",\"" + BuffName + "\")", "abc.lua");
             if (Equals(null, b))
              {
                  return false;
              }
              else
              {
                  return true;
              }*/
        }
        //Has Your Debuff
        public static bool HasDebuff(string buffName)
        {
            return Target.IsDebuffOnTarget(buffName);

            /*List<string> b = Lua.GetReturnValues("return UnitDebuff(\"target\",\"" + BuffName + "\")", "abc.lua");

            if (Equals(null, b))
            {
                return false;
            }

            string rank = b[1];

            List<string> c = Lua.GetReturnValues("return UnitDebuff(\"target\",\"" + BuffName + "\"," + rank + ",\"PLAYER\")", "abc.lua");
            if (Equals(null, c))
            {
                return false;
            }
            else
            {
                return true;
            }*/
        }
        //Stacks of Debuff
        public static int DebuffStacks(string buffName)
        {
            List<string> b = Lua.GetReturnValues("return UnitDebuff(\"target\",\"" + buffName + "\")", "abc.lua");

            if (Equals(null, b))
            {
                return 0;
            }

            string rank = b[1];

            List<string> c = Lua.GetReturnValues("return UnitDebuff(\"target\",\"" + buffName + "\",\"" + rank + "\",\"PLAYER\")", "abc.lua");
            if (Equals(null, c))
            {
                return 0;
            }

            return Convert.ToInt32(c[3]);
        }
        //Debuff Time Remaining
        public static double DebuffTimeRemining(string buffName)
        {
            List<string> a = Lua.GetReturnValues("return GetTime()", "abc.lua");
            List<string> b = Lua.GetReturnValues("return UnitDebuff(\"target\",\"" + buffName + "\")", "abc.lua");

            if (Equals(null, b))
            {
                return 0;
            }

            string rank = b[1];

            List<string> c = Lua.GetReturnValues("return UnitDebuff(\"target\",\"" + buffName + "\",\"" + rank + "\",\"PLAYER\")", "abc.lua");
            if (Equals(null, c))
            {
                return 0;
            }

            return Convert.ToDouble(c[6]) - Convert.ToDouble(a[0]);
        }

        public static int DebuffTimeRemining(this WoWUnit onUnit, string auraName, bool fromMyAura)
        {
            WoWAura wantedAura =
                onUnit.GetAllAuras().FirstOrDefault(a => a.Name == auraName && (!fromMyAura || a.CreatorGuid == StyxWoW.Me.Guid));

            if (wantedAura != null)
            {
                return wantedAura.TimeLeft.Seconds;
            }
            return 0;
        }

        //Debuff on Self
        public static bool MeHasDebuff(string buffName)
        {
            List<string> b = Lua.GetReturnValues("return UnitDebuff(\"player\",\"" + buffName + "\")", "abc.lua");
            if (Equals(null, b))
            {
                return false;
            }
            return true;
        }
        //Debuff Stacks on Self
        public static int MeDebuffStacks(string buffName)
        {
            List<string> b = Lua.GetReturnValues("return UnitDebuff(\"player\",\"" + buffName + "\")", "abc.lua");
            if (Equals(null, b))
            {
                return 0;
            }

            return Convert.ToInt32(b[3]);
        }

        public static bool HasAura(this WoWUnit unit, string aura)
        {
            return HasAura(unit, aura, 0);
        }

        public static bool HasAura(this WoWUnit unit, string aura, int stacks)
        {
            return HasAura(unit, aura, stacks, null);
        }

        private static bool HasAura(this WoWUnit unit, string aura, int stacks, WoWUnit creator)
        {
            return unit.GetAllAuras().Any(a => a.Name == aura && a.StackCount >= stacks &&
                                              (creator == null || a.CreatorGuid == creator.Guid));
        }

        public static float MeleeRange
        {
            get
            {
                // If we have no target... then give nothing.
                return StyxWoW.Me.CurrentTargetGuid == 0 ? 0f : Math.Max(5f, StyxWoW.Me.CombatReach + 1.3333334f + StyxWoW.Me.CurrentTarget.CombatReach);
            }
        }

        /*-------------------------
        --------HP/MP Checks-------
        -------------------------*/
        public static double HealthPercent(WoWUnit unit)
        {
            return unit.HealthPercent;
        }

        public static double ManaPercent(WoWUnit unit)
        {
            return unit.ManaPercent;
        }

        public static double GetCurrentRestHealth()
        {
            if (Me.Class == WoWClass.Priest)
                return PriestSettings.RestHealth;
            if (Me.Class == WoWClass.Shaman)
                return ShamanSettings.RestHealth;
            if (Me.Class == WoWClass.Druid)
                return DruidSettings.RestHealth;

            return 40;
        }

        public static double GetCurrentRestMana()
        {
            if (Me.Class == WoWClass.Priest)
                return PriestSettings.RestMana;
            if (Me.Class == WoWClass.Shaman)
                return ShamanSettings.RestMana;
            if (Me.Class == WoWClass.Druid)
                return DruidSettings.RestMana;

            return 40;
        }

        public static string GetLazyRaiderSettings()
        {
            if (Me.Class == WoWClass.Priest)
                return PriestSettings.LazyRaider;
            if (Me.Class == WoWClass.Shaman)
                return ShamanSettings.LazyRaider;
            if (Me.Class == WoWClass.Druid)
                return DruidSettings.LazyRaider;

            return GetLazyRaiderSettings();
        }

        public static Composite RestBehaviour()
        {
            return

                // Don't fucking run the rest behavior (or any other) if we're dead or a ghost. Thats all.
                new Decorator(
                    ret => !StyxWoW.Me.Dead && !StyxWoW.Me.IsGhost,
                    new PrioritySelector(
                // Make sure we wait out res sickness. Fuck the classes that can deal with it. :O
                        new Decorator(
                            ret => StyxWoW.Me.HasAura("Resurrection Sickness"),
                            new Action(ret => { })),
                // Check if we're allowed to eat (and make sure we have some food. Don't bother going further if we have none.
                        new Decorator(
                            ret =>
                            !StyxWoW.Me.IsSwimming && StyxWoW.Me.HealthPercent <= GetCurrentRestHealth() && !StyxWoW.Me.HasAura("Food") &&
                            Consumable.GetBestFood(false) != null,
                            new PrioritySelector(
                                new Decorator(
                                    ret => StyxWoW.Me.IsMoving,
                                    new Action(ret => Navigator.PlayerMover.MoveStop())),
                                new Action(
                                    ret =>
                                    {
                                        Styx.Logic.Common.Rest.FeedImmediate();
                                        StyxWoW.SleepForLagDuration();
                                    })
                                )),
                // Make sure we're a class with mana, if not, just ignore drinking all together! Other than that... same for food.
                        new Decorator(
                            ret =>
                            !StyxWoW.Me.IsSwimming && StyxWoW.Me.PowerType == WoWPowerType.Mana &&
                            StyxWoW.Me.ManaPercent <= GetCurrentRestMana() &&
                            !StyxWoW.Me.HasAura("Drink") && Consumable.GetBestDrink(false) != null,
                            new PrioritySelector(
                                new Decorator(
                                    ret => StyxWoW.Me.IsMoving,
                                    new Action(ret => Navigator.PlayerMover.MoveStop())),
                                new Action(
                                    ret =>
                                    {
                                        Styx.Logic.Common.Rest.DrinkImmediate();
                                        StyxWoW.SleepForLagDuration();
                                    })
                                )),
                // This is to ensure we STAY SEATED while eating/drinking. No reason for us to get up before we have to.
                        new Decorator(
                            ret =>
                            (StyxWoW.Me.HasAura("Food") && StyxWoW.Me.HealthPercent < 95) ||
                            (StyxWoW.Me.HasAura("Drink") && StyxWoW.Me.PowerType == WoWPowerType.Mana && StyxWoW.Me.ManaPercent < 95),
                            new ActionAlwaysSucceed()),
                        new Decorator(
                            ret =>
                            ((StyxWoW.Me.PowerType == WoWPowerType.Mana && StyxWoW.Me.ManaPercent <= GetCurrentRestMana()) ||
                            StyxWoW.Me.HealthPercent <= GetCurrentRestHealth()) && !StyxWoW.Me.CurrentMap.IsBattleground,
                            new Action(ret => LogGreenMessage("We have no food/drink. Waiting to recover our health/mana back")))
                        ));
        }

        #region Eat
        public class NeedToEat : Decorator
        {
            public NeedToEat(Composite child) : base(child) { }

            protected override bool CanRun(object context)
            {
                if (Me.IsSwimming) return false;
                if (Self.IsBuffOnMe("Innervate")) return false; // Druid only
                if (Self.IsBuffOnMe("Dispersion")) return false; // Priest

                return (!Self.IsBuffOnMe("Food") && Me.HealthPercent < PriestSettings.RestHealth);
            }
        }

        public class Eat : Action
        {
            protected override RunStatus Run(object context)
            {
                bool result = false;

                if (Me.IsMoving)
                {
                    WoWMovement.MoveStop();
                    Thread.Sleep(250);
                }

                Styx.Logic.Common.Rest.FeedImmediate();
                //Inventory.Eat();

                if (Self.IsBuffOnMe("Food")) result = true;

                return result ? RunStatus.Success : RunStatus.Failure;
            }
        }
        #endregion

        #region Drink
        public class NeedToDrink : Decorator
        {
            public NeedToDrink(Composite child) : base(child) { }

            protected override bool CanRun(object context)
            {
                if (Me.IsSwimming) return false;
                if (Self.IsBuffOnMe("Innervate")) return false; // Druid only
                if (Self.IsBuffOnMe("Dispersion")) return false; // Priest



                return (!Self.IsBuffOnMe("Drink") && Me.ManaPercent < PriestSettings.RestMana);
            }
        }

        public class Drink : Action
        {
            protected override RunStatus Run(object context)
            {
                bool result = false;

                if (Me.IsMoving)
                {
                    WoWMovement.MoveStop();
                    Thread.Sleep(250);
                }

                Styx.Logic.Common.Rest.DrinkImmediate();

                if (Self.IsBuffOnMe("Drink")) result = true;

                return result ? RunStatus.Success : RunStatus.Failure;
            }
        }
        #endregion

        #region Cancel Drink Buff
        public class NeedToCancelDrink : Decorator
        {
            public NeedToCancelDrink(Composite child) : base(child) { }

            protected override bool CanRun(object context)
            {
                //if (Me.IsInInstance && PriestSettings.PartyHealWhen.Contains("dedicated") && Self.IsPowerPercentAbove(50) && (RAF.PartyTankRole != null && RAF.PartyTankRole.HealthPercent < 65)) return true;
                return Self.IsPowerPercentAbove(98) && Self.IsBuffOnMe("Drink");
            }
        }

        public class CancelDrink : Action
        {
            protected override RunStatus Run(object context)
            {
                Lua.DoString("CancelUnitBuff('player', 'Drink')");
                return RunStatus.Success;
            }
        }
        #endregion

        #region Cancel Food Buff
        public class NeedToCancelFood : Decorator
        {
            public NeedToCancelFood(Composite child) : base(child) { }

            protected override bool CanRun(object context)
            {
                return Self.IsHealthPercentAbove(98) && Self.IsBuffOnMe("Food");
            }
        }

        public class CancelFood : Action
        {
            protected override RunStatus Run(object context)
            {
                Lua.DoString("CancelUnitBuff('player', 'Food')");
                return RunStatus.Success;
            }
        }
        #endregion

        public static Composite CreateMoveToTargetBehavior(bool stopInRange, float range)
        {
            if (Me.CurrentTarget == null)
                return new Action(akatosh => RunStatus.Failure);

            return CreateMoveToLocationBehavior(ret => StyxWoW.Me.CurrentTarget.Location, stopInRange, range);
        }

        public static Composite CreateMoveToMeleeBehavior(bool stopInRange)
        {
            return CreateMoveToMeleeBehavior(ret => StyxWoW.Me.CurrentTarget.Location, stopInRange);
        }

        public static Composite CreateMoveToLocationBehavior(LocationRetriever location, bool stopInRange, float range)
        {
            // Do not fuck with this. It will ensure we stop in range if we're supposed to.
            // Otherwise it'll stick to the targets ass like flies on dog shit.
            // Specifying a range of, 2 or so, will ensure we're constantly running to the target. Specifying 0 will cause us to spin in circles around the target
            // or chase it down like mad. (PVP oriented behavior)
            return
                new Decorator(
                // Don't run if the movement is disabled.
                    ret => !GetLazyRaiderSettings().Contains("always"),
                    new PrioritySelector(
                        new Decorator(
                // Give it a little more than 1/2 a yard buffer to get it right. CTM is never 'exact' on where we land. So don't expect it to be.
                            ret => stopInRange && StyxWoW.Me.Location.Distance(location(ret)) + 0.6f < range,
                            new PrioritySelector(
                                new Action(ret => Navigator.PlayerMover.MoveStop()),
                // In short; if we're not moving, just 'succeed' here, so we break the tree.
                                new Action(ret => RunStatus.Success)
                                )
                            ),
                        new Action(ret => Navigator.MoveTo(location(ret)))
                        ));
        }



        public static Composite CreateMoveToMeleeBehavior(LocationRetriever location, bool stopInRange)
        {
            return CreateMoveToLocationBehavior(location, stopInRange, Spell.SafeMeleeRange);
        }

        /*-------------------------
        --------Spell Functions----
        -------------------------*/
        //Casts a Spell. 

        public static bool HasWeaponImbue(WoWInventorySlot slot, string imbueName)
        {
            //Logger.Write("Checking Weapon Imbue on " + slot + " for " + imbueName);
            var item = StyxWoW.Me.Inventory.Equipped.GetEquippedItem(slot);
            if (item == null)
            {
                //Logger.Write("We have no " + slot + " equipped!");
                return true;
            }

            var enchant = item.TemporaryEnchantment;
            //if (enchant != null)
            //    Logger.Write("Enchant: " + enchant.Name + " - " + (enchant.Name == imbueName));
            return enchant != null && enchant.Name == imbueName;
        }

        public static TimeSpan GetAuraTimeLeft(this WoWUnit onUnit, string auraName, bool fromMyAura)
        {
            WoWAura wantedAura =
                onUnit.GetAllAuras().FirstOrDefault(a => a.Name == auraName && (!fromMyAura || a.CreatorGuid == StyxWoW.Me.Guid));

            return wantedAura != null ? wantedAura.TimeLeft : TimeSpan.Zero;
        }

        public static WoWSpell GetSpellFrom(int id)
        {
            return WoWSpell.FromId(id);
        }

        public static WoWSpell LowestLevelRangeSpell()
        {
            if(Me.Class == WoWClass.Priest)
            {
                return WoWSpell.FromId(585);
            }
            else if(Me.Class == WoWClass.Shaman)
                return WoWSpell.FromId(403);

            return WoWSpell.FromId(585);
        }

        #region PreventDoubleCast

        /// <summary>
        /// Creates a composite to avoid double casting spells on current target. Mostly usable for spells like Immolate, Devouring Plague etc.
        /// </summary>
        /// <remarks>
        /// Created 19/12/2011 raphus
        /// </remarks>
        /// <param name="spellNames"> Spell names to check </param>
        /// <returns></returns>
        public static Composite PreventDoubleCast(params string[] spellNames)
        {
            return PreventDoubleCast(ret => StyxWoW.Me.CurrentTarget, spellNames);
        }

        /// <summary>
        /// Creates a composite to avoid double casting spells on specified unit. Mostly usable for spells like Immolate, Devouring Plague etc.
        /// </summary>
        /// <remarks>
        /// Created 19/12/2011 raphus
        /// </remarks>
        /// <param name="unit"> Unit to check </param>
        /// <param name="spellNames"> Spell names to check </param>
        /// <returns></returns>
        public static Composite PreventDoubleCast(UnitSelectionDelegate unit, params string[] spellNames)
        {
            return
                new PrioritySelector(
                    new Decorator(
                        ret => StyxWoW.Me.IsCasting && spellNames.Contains(StyxWoW.Me.CastingSpell.Name) &&
                               unit != null && unit(ret) != null && unit(ret).Auras.Any(a => a.Value.SpellId == StyxWoW.Me.CastingSpellId &&
                               a.Value.CreatorGuid == StyxWoW.Me.Guid),
                        new Action(ret => SpellManager.StopCasting())));
        }

        #endregion

        #region Cast - by name

        public static Composite Cast(string name)
        {
            return Cast(name, ret => true);
        }

        public static Composite Cast(string name, SimpleBooleanDelegate requirements)
        {
            return Cast(name, ret => true, ret => StyxWoW.Me.CurrentTarget, requirements);
        }

        public static Composite Cast(string name, UnitSelectionDelegate onUnit)
        {
            return Cast(name, ret => true, onUnit, ret => true);
        }

        public static Composite Cast(string name, UnitSelectionDelegate onUnit, SimpleBooleanDelegate requirements)
        {
            return Cast(name, ret => true, onUnit, requirements);
        }


        public static Composite Cast(string name, SimpleBooleanDelegate checkMovement, UnitSelectionDelegate onUnit, SimpleBooleanDelegate requirements)
        {
            return new Decorator(
                ret =>
                {

                    var minReqs = requirements != null && onUnit != null && requirements(ret) && onUnit(ret) != null;
                    var canCast = false;
                    var inRange = false;
                    if (minReqs)
                    {
                        canCast = SpellManager.CanCast(name, onUnit(ret), false, checkMovement(ret));

                        if (canCast)
                        {
                            var target = onUnit(ret);
                            // We're always in range of ourselves. So just ignore this bit if we're casting it on us
                            if (target.IsMe)
                            {
                                inRange = true;
                            }
                            else
                            {
                                WoWSpell spell;
                                if (SpellManager.Spells.TryGetValue(name, out spell))
                                {
                                    var rangeId = spell.InternalInfo.SpellRangeId;
                                    var minRange = spell.MinRange;
                                    var maxRange = spell.MaxRange;
                                    // RangeId 1 is "Self Only". This should make life easier for people to use self-buffs, or stuff like Starfall where you cast it as a pseudo-buff.
                                    if (rangeId == 1)
                                        inRange = true;
                                    // RangeId 2 is melee range. Huzzah :)
                                    else if (rangeId == 2)
                                        inRange = target.Distance < MeleeRange;
                                    else
                                        inRange = target.Distance < maxRange &&
                                                  target.Distance > (minRange == 0 ? minRange : minRange + 3);
                                }
                            }
                        }
                    }

                    return minReqs && canCast && inRange;
                },
                new Action(
                    ret =>
                    {
                        LogBlueMessage("Casting - " + name);
                        SpellManager.Cast(name, onUnit(ret));
                    })
                );
        }

        #endregion

        #region Cast - by ID

        /// <summary>
        ///   Creates a behavior to cast a spell by ID. Returns RunStatus.Success if successful,
        ///   RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "spellId">Identifier for the spell.</param>
        /// <returns>.</returns>
        public static Composite Cast(int spellId)
        {
            return Cast(spellId, ret => true);
        }

        /// <summary>
        ///   Creates a behavior to cast a spell by ID, with special requirements. Returns
        ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "spellId">Identifier for the spell.</param>
        /// <param name = "requirements">The requirements.</param>
        /// <returns>.</returns>
        public static Composite Cast(int spellId, SimpleBooleanDelegate requirements)
        {
            return Cast(spellId, ret => StyxWoW.Me.CurrentTarget, requirements);
        }

        /// <summary>
        ///   Creates a behavior to cast a spell by ID, on a specific unit. Returns
        ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "spellId">Identifier for the spell.</param>
        /// <param name = "onUnit">The on unit.</param>
        /// <returns>.</returns>
        public static Composite Cast(int spellId, UnitSelectionDelegate onUnit)
        {
            return Cast(spellId, onUnit, ret => true);
        }

        /// <summary>
        ///   Creates a behavior to cast a spell by ID, with special requirements, on a specific unit.
        ///   Returns RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "spellId">Identifier for the spell.</param>
        /// <param name = "onUnit">The on unit.</param>
        /// <param name = "requirements">The requirements.</param>
        /// <returns>.</returns>
        public static Composite Cast(int spellId, UnitSelectionDelegate onUnit, SimpleBooleanDelegate requirements)
        {
            return new Decorator(
                ret =>
                requirements != null && requirements(ret) && onUnit != null && onUnit(ret) != null && SpellManager.CanCast(spellId, onUnit(ret), true),
                new Action(
                    ret =>
                    {
                        LogBlueMessage("Casting - " + spellId);
                        SpellManager.Cast(spellId);
                    })
                );
        }

        #endregion

        #region Buff - by name

        /// <summary>
        ///   Creates a behavior to cast a buff by name on current target. Returns
        ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "name">The name of the buff</param>
        /// <returns></returns>
        public static Composite Buff(string name)
        {
            return Buff(name, ret => true);
        }

        /// <summary>
        ///   Creates a behavior to cast a buff by name, with special requirements, on current target. Returns
        ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "name">The name of the buff</param>
        /// <param name = "requirements">The requirements.</param>
        /// <returns></returns>
        public static Composite Buff(string name, SimpleBooleanDelegate requirements)
        {
            return Buff(name, ret => StyxWoW.Me.CurrentTarget, requirements);
        }

        /// <summary>
        ///   Creates a behavior to cast a buff by name on a specific unit. Returns
        ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "name">The name of the buff</param>
        /// <param name = "onUnit">The on unit</param>
        /// <returns></returns>
        public static Composite Buff(string name, UnitSelectionDelegate onUnit)
        {
            return Buff(name, onUnit, ret => true);
        }

        /// <summary>
        ///   Creates a behavior to cast a buff by name, with special requirements, on a specific unit. Returns
        ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "name">The name of the buff</param>
        /// <param name = "onUnit">The on unit</param>
        /// <param name = "requirements">The requirements.</param>
        /// <returns></returns>
        public static Composite Buff(string name, UnitSelectionDelegate onUnit, SimpleBooleanDelegate requirements)
        {
            return
                new Decorator(
                    ret => onUnit(ret) != null && !onUnit(ret).HasAura(name),
                    Cast(name, onUnit, requirements));
        }

        #endregion

        #region BuffSelf - by name

        /// <summary>
        ///   Creates a behavior to cast a buff by name on yourself. Returns
        ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/6/2011.
        /// </remarks>
        /// <param name = "name">The buff name.</param>
        /// <returns>.</returns>
        public static Composite BuffSelf(string name)
        {
            return Buff(name, ret => StyxWoW.Me, ret => true);
        }

        /// <summary>
        ///   Creates a behavior to cast a buff by name on yourself with special requirements. Returns RunStatus.Success if
        ///   successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/6/2011.
        /// </remarks>
        /// <param name = "name">The buff name.</param>
        /// <param name = "requirements">The requirements.</param>
        /// <returns>.</returns>
        public static Composite BuffSelf(string name, SimpleBooleanDelegate requirements)
        {
            return Buff(name, ret => StyxWoW.Me, requirements);
        }

        #endregion

        #region Buff - by ID

        /// <summary>
        ///   Creates a behavior to cast a buff by name on current target. Returns
        ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "spellId">The ID of the buff</param>
        /// <returns></returns>
        public static Composite Buff(int spellId)
        {
            return Buff(spellId, ret => true);
        }

        /// <summary>
        ///   Creates a behavior to cast a buff by name, with special requirements, on current target. Returns
        ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "spellId">The ID of the buff</param>
        /// <param name = "requirements">The requirements.</param>
        /// <returns></returns>
        public static Composite Buff(int spellId, SimpleBooleanDelegate requirements)
        {
            return Buff(spellId, ret => StyxWoW.Me.CurrentTarget, requirements);
        }

        /// <summary>
        ///   Creates a behavior to cast a buff by name on a specific unit. Returns
        ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "spellId">The ID of the buff</param>
        /// <param name = "onUnit">The on unit</param>
        /// <returns></returns>
        public static Composite Buff(int spellId, UnitSelectionDelegate onUnit)
        {
            return Buff(spellId, onUnit, ret => true);
        }

        /// <summary>
        ///   Creates a behavior to cast a buff by name, with special requirements, on a specific unit. Returns
        ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "spellId">The ID of the buff</param>
        /// <param name = "onUnit">The on unit</param>
        /// <param name = "requirements">The requirements.</param>
        /// <returns></returns>
        public static Composite Buff(int spellId, UnitSelectionDelegate onUnit, SimpleBooleanDelegate requirements)
        {
            return
                new Decorator(
                    ret => onUnit(ret) != null && !onUnit(ret).Auras.Values.Any(a => a.SpellId == spellId),
                    Cast(spellId, onUnit, requirements));
        }

        #endregion

        #region BufSelf - by ID

        /// <summary>
        ///   Creates a behavior to cast a buff by ID on yourself. Returns
        ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/6/2011.
        /// </remarks>
        /// <param name = "spellId">The buff ID.</param>
        /// <returns>.</returns>
        public static Composite BuffSelf(int spellId)
        {
            return Buff(spellId, ret => true);
        }

        /// <summary>
        ///   Creates a behavior to cast a buff by ID on yourself with special requirements. Returns RunStatus.Success if
        ///   successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/6/2011.
        /// </remarks>
        /// <param name = "spellId">The buff ID.</param>
        /// <param name = "requirements">The requirements.</param>
        /// <returns>.</returns>
        public static Composite BuffSelf(int spellId, SimpleBooleanDelegate requirements)
        {
            return Buff(spellId, ret => StyxWoW.Me, requirements);
        }

        #endregion

        #region Heal - by name

        /// <summary>
        ///   Creates a behavior to cast a heal spell by name. Heal behaviors will make sure
        ///   we don't double cast. Returns RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "name">The name.</param>
        /// <returns>.</returns>
        public static Composite Heal(string name)
        {
            return Heal(name, ret => true);
        }

        /// <summary>
        ///   Creates a behavior to cast a heal spell by name, with special requirements. Heal behaviors will make sure
        ///   we don't double cast. Returns RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "name">The name.</param>
        /// <param name = "requirements">The requirements.</param>
        /// <returns>.</returns>
        public static Composite Heal(string name, SimpleBooleanDelegate requirements)
        {
            return Heal(name, ret => true, ret => StyxWoW.Me.CurrentTarget, requirements);
        }

        /// <summary>
        ///   Creates a behavior to cast a heal spell by name, on a specific unit. Heal behaviors will make sure
        ///   we don't double cast. Returns RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "name">The name.</param>
        /// <param name = "onUnit">The on unit.</param>
        /// <returns>.</returns>
        public static Composite Heal(string name, UnitSelectionDelegate onUnit)
        {
            return Heal(name, ret => true, onUnit, ret => true);
        }
        /// <summary>
        ///   Creates a behavior to cast a heal spell by name, on a specific unit. Heal behaviors will make sure
        ///   we don't double cast. Returns RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "name">The name.</param>
        /// <param name = "onUnit">The on unit.</param>
        /// <param name = "requirements">The requirements.</param>
        /// <returns>.</returns>
        public static Composite Heal(string name, UnitSelectionDelegate onUnit, SimpleBooleanDelegate requirements)
        {
            return Heal(name, ret => true, onUnit, requirements);
        }

        /// <summary>
        ///   Creates a behavior to cast a heal spell by name, with special requirements, on a specific unit. Heal behaviors will make sure
        ///   we don't double cast. Returns RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "name">The name.</param>
        /// <param name="checkMovement"></param>
        /// <param name = "onUnit">The on unit.</param>
        /// <param name = "requirements">The requirements.</param>
        /// <returns>.</returns>
        public static Composite Heal(string name, SimpleBooleanDelegate checkMovement, UnitSelectionDelegate onUnit, SimpleBooleanDelegate requirements)
        {
            return
                new Sequence(
                    Cast(name, checkMovement, onUnit, requirements),
                // Little bit wait here to catch casting
                    new WaitContinue(
                        1,
                        ret =>
                        {
                            WoWSpell spell;
                            if (SpellManager.Spells.TryGetValue(name, out spell))
                            {
                                if (spell.CastTime == 0)
                                    return true;

                                return StyxWoW.Me.IsCasting;
                            }

                            return true;
                        },
                        new ActionAlwaysSucceed()),
                    new WaitContinue(
                        10,
                        ret =>
                        {
                            // Let channeled heals been cast till end.
                            if (StyxWoW.Me.ChanneledCastingSpellId != 0)
                            {
                                return false;
                            }

                            // Interrupted or finished casting. Continue
                            if (!StyxWoW.Me.IsCasting)
                            {
                                return true;
                            }

                            // 500ms left till cast ends. Shall continue for next spell
                            //if (StyxWoW.Me.CurrentCastTimeLeft.TotalMilliseconds < 500)
                            //{
                            //    return true;
                            //}

                            // If requirements don't meet anymore, stop casting and let it continue
                            if (!requirements(ret))
                            {
                                SpellManager.StopCasting();
                                return true;
                            }
                            return false;
                        },
                        new ActionAlwaysSucceed()));
        }

        #endregion

        #region CastOnGround - placeable spell casting

        public delegate WoWPoint LocationRetriever(object context);

        /// <summary>
        ///   Creates a behavior to cast a spell by name, on the ground at the specified location. Returns
        ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "spell">The spell.</param>
        /// <param name = "onLocation">The on location.</param>
        /// <returns>.</returns>
        public static Composite CastOnGround(string spell, LocationRetriever onLocation)
        {
            return CastOnGround(spell, onLocation, ret => true);
        }

        /// <summary>
        ///   Creates a behavior to cast a spell by name, on the ground at the specified location. Returns RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "spell">The spell.</param>
        /// <param name = "onLocation">The on location.</param>
        /// <param name = "requirements">The requirements.</param>
        /// <returns>.</returns>
        public static Composite CastOnGround(string spell, LocationRetriever onLocation, SimpleBooleanDelegate requirements)
        {
            return new Decorator(
                ret =>
                requirements(ret) && onLocation != null && SpellManager.CanCast(spell)
                /*&&
                SpellManager.Spells[spell].MaxRange < StyxWoW.Me.Location.Distance(onLocation(ret))*/,
                new Action(
                    ret =>
                    {
                        Logger.Write("Casting " + spell + " at location " + onLocation(ret));
                        SpellManager.Cast(spell);
                        LegacySpellManager.ClickRemoteLocation(onLocation(ret));
                    })
                );
        }

        #endregion


        public static RunStatus CastTarget(string spellName, string target)
        {
            if (SpellManager.CanCast(spellName))
            {
                Lua.DoString("CastSpellByName(\"" + spellName + "\",\"" + target + "\")");
                return RunStatus.Success;
            }
            return RunStatus.Failure;
        }

		public static bool Usable(string spellName)
		{
			List<string> a = Lua.GetReturnValues("return IsUsableSpell(\"" + spellName + "\")", "abc.lua");
			
			if (a[0] == "1" && a[1] == "")
			{
				if (CoolDown(spellName) < .25)
				{
					return true;
				}
			}
			
			return false;
		}
		
        //Returns Cooldown of the Spell
        public static double CoolDown(string spellName)
        {
            List<string> a = Lua.GetReturnValues("return GetTime()", "abc.lua");
            List<string> b = Lua.GetReturnValues("return GetSpellCooldown(\"" + spellName + "\")", "abc.lua");

            if (Equals(b, null))
            {
                return 9999;
            }

            if (b[0] == "0")
            {
                return 0;
            }
            return Convert.ToDouble(b[1]) - (Convert.ToDouble(a[0]) - Convert.ToDouble(b[0]));
        }

        //Returns if you are currently casting.
        public static bool Casting()
        {
            List<string> a = Lua.GetReturnValues("return GetTime()", "abc.lua");
            List<string> b = Lua.GetReturnValues("return UnitCastingInfo(\"player\")", "abc.lua");
            List<string> c = Lua.GetReturnValues("return UnitChannelInfo(\"player\")", "abc.lua");
            List<string> d = Lua.GetReturnValues("return GetNetStats()", "abc.lua");

            if (!Equals(b, null))
            {
                if (((Convert.ToDouble(b[5]) - 30) - (Convert.ToDouble(a[0]) * 1000) - (Convert.ToDouble(d[3]))) < 0)
                {
                    return false;
                }
                return true;
            }

            if (!Equals(c, null))
            {
                if (c[0] == "Drain Soul")
                {
                    return false;
                }

                if (((Convert.ToDouble(c[5]) - 30) - (Convert.ToDouble(a[0]) * 1000) - (Convert.ToDouble(d[3]))) < 0)
                {
                    return false;
                }
                return true;
            }

            return false;
        }

        public static RunStatus Interrupt(string spellName)
        {
            if (!SpellManager.CanCast(spellName))
            {
                return RunStatus.Failure;
            }
            List<string> a = Lua.GetReturnValues("return UnitExists(\"focus\")", "abc.lua");
            List<string> b = Lua.GetReturnValues("return UnitCanAttack(\"player\",\"focus\")", "abc.lua");
            List<string> c = Lua.GetReturnValues("return UnitName(\"focus\")", "abc.lua");
            List<string> d = Lua.GetReturnValues("return UnitCastingInfo(\"target\")", "abc.lua");
            List<string> e = Lua.GetReturnValues("return UnitChannelInfo(\"target\")", "abc.lua");
            List<string> f = Lua.GetReturnValues("return UnitCastingInfo(\"focus\")", "abc.lua");
            List<string> g = Lua.GetReturnValues("return UnitChannelInfo(\"focus\")", "abc.lua");
            List<string> h = Lua.GetReturnValues("return UnitCanAttack(\"player\",\"target\")", "abc.lua");
            List<string> i = Lua.GetReturnValues("return UnitExists(\"target\")", "abc.lua");

            if (a[0] != "" && b[0] != "")
            {
                if (f[8] != "1")
                {
                    if (!InterruptBlacklist.InterruptBlist[c[0]].Contains(f[0]))
                    {
                        return CastTarget(spellName, "focus");
                    }
                }

                if (g[7] != "1")
                {

                    if (!InterruptBlacklist.InterruptBlist[c[0]].Contains(g[0]))
                    {
                        return CastTarget(spellName, "focus");
                    }
                }
            }

            if (i[0] != "" && h[0] != "")
            {
                string name = Me.CurrentTarget.Name;
                if (d[8] != "1")
                {
                    if (!InterruptBlacklist.InterruptBlist[name].Contains(d[0]))
                    {
                        Cast(spellName);
                    }
                }

                if (e[7] != "1")
                {
                    if (!InterruptBlacklist.InterruptBlist[name].Contains(e[0]))
                    {
                        Cast(spellName);
                    }
                }
            }

            return RunStatus.Failure;
        }

        public static bool PetInterrupt(string spellName)
        {
            if (!(PetUsable(spellName)))
            {
                return false;
            }
            List<string> a = Lua.GetReturnValues("return UnitExists(\"focus\")", "abc.lua");
            List<string> b = Lua.GetReturnValues("return UnitCanAttack(\"player\",\"focus\")", "abc.lua");
            List<string> c = Lua.GetReturnValues("return UnitName(\"focus\")", "abc.lua");
            List<string> d = Lua.GetReturnValues("return UnitCastingInfo(\"target\")", "abc.lua");
            List<string> e = Lua.GetReturnValues("return UnitChannelInfo(\"target\")", "abc.lua");
            List<string> f = Lua.GetReturnValues("return UnitCastingInfo(\"focus\")", "abc.lua");
            List<string> g = Lua.GetReturnValues("return UnitChannelInfo(\"focus\")", "abc.lua");
            List<string> h = Lua.GetReturnValues("return UnitCanAttack(\"player\",\"target\")", "abc.lua");
            List<string> i = Lua.GetReturnValues("return UnitExists(\"target\")", "abc.lua");

            if (a[0] != "" && b[0] != "")
            {
                if (f[8] != "1")
                {
                    if (!InterruptBlacklist.InterruptBlist[c[0]].Contains(f[0]))
                    {
                        return PetCastFocus(spellName);
                    }
                }

                if (g[7] != "1")
                {

                    if (!InterruptBlacklist.InterruptBlist[c[0]].Contains(g[0]))
                    {
                        return PetCastFocus(spellName);
                    }
                }
            }

            if (i[0] != "" && h[0] != "")
            {
                string name = Me.CurrentTarget.Name;
                if (d[8] != "1")
                {
                    if (!InterruptBlacklist.InterruptBlist[name].Contains(d[0]))
                    {
                        return PetCast(spellName);
                    }
                }

                if (e[7] != "1")
                {
                    if (!InterruptBlacklist.InterruptBlist[name].Contains(e[0]))
                    {
                        return PetCast(spellName);
                    }
                }
            }

            return false;
        }

        public static bool PetUsable(string action)
        {
            if (!Me.GotAlivePet)
            {
                return false;
            }
            PetSpells.Clear();
            PetSpells.AddRange(StyxWoW.Me.PetSpells);
            WoWPetSpell petAction = PetSpells.FirstOrDefault(p => p.ToString() == action);
            if (petAction == null || petAction.Spell == null)
            {
                return false;
            }

            return !petAction.Spell.Cooldown;
        }

        public static bool PetCast(string petSpell)
        {
            if (!PetUsable(petSpell))
            {
                return false;
            }

            var spell = PetSpells.FirstOrDefault(p => p.ToString() == petSpell);
            if (spell == null)
                return false;

            Lua.DoString("CastPetAction({0})", spell.ActionBarIndex + 1);
            return true;
        }

        public static bool PetCastFocus(string petSpell)
        {
            if (!PetUsable(petSpell))
            {
                return false;
            }
            var spell = PetSpells.FirstOrDefault(p => p.ToString() == petSpell);
            if (spell == null)
                return false;

            Lua.DoString("CastPetAction({0}, 'focus')", spell.ActionBarIndex + 1);
            return true;
        }

// ReSharper disable InconsistentNaming
        public static bool GCD()
// ReSharper restore InconsistentNaming
        {
            List<string> a = Lua.GetReturnValues("return GetSpellCooldown(\"61304\")", "abc.lua");

            if (a[0] == "0")
            {
                return false;
            }

            return true;
        }

        public static bool HasAnyAura(this WoWUnit unit, params string[] auraNames)
        {
            var auras = unit.GetAllAuras();
            var hash = new HashSet<string>(auraNames);
            return auras.Any(a => hash.Contains(a.Name));
        }

        public static bool AoE()
        {
            return false;
        }


        #region LOS Check
        public class NeedToLOSCheck : Decorator
        {
            public NeedToLOSCheck(Composite child) : base(child) { }

            protected override bool CanRun(object context)
            {
                if (!Me.GotTarget || Targ.Dead) return false;
                bool result = !Targ.InLineOfSpellSight;

                return result;
            }
        }

        public class LOSCheck : Action
        {
            protected override RunStatus Run(object context)
            {
                if (Me.IsInInstance)
                {
                    Movement.MoveTo(1);
                    Thread.Sleep(250);

                    while (!Targ.InLineOfSpellSight)
                    {
                        Movement.MoveTo(Targ.Location);
                        Thread.Sleep(250);
                    }
                    Movement.StopMoving();
                }
                else
                {
                    Movement.MoveTo(1);
                    Thread.Sleep(250);

                    while (!Targ.InLineOfSpellSight)
                    {
                        Movement.MoveTo(Targ.Location);
                        Thread.Sleep(250);
                    }
                    Movement.StopMoving();
                    Target.Face();
                }

                return RunStatus.Success;
            }
        }
        #endregion

        #region Auto Attack
        public class NeedToAutoAttack : Decorator
        {
            public NeedToAutoAttack(Composite child) : base(child) { }

            protected override bool CanRun(object context)
            {
                return (Me.GotTarget && Targ.IsAlive && !Me.IsAutoAttacking && Target.IsWithinInteractRange);
            }
        }

        public class AutoAttack : Action
        {
            protected override RunStatus Run(object context)
            {
                Utils.AutoAttack(true);

                return RunStatus.Failure;
            }
        }
        #endregion

        #region Retarget
        public class NeedToRetarget : Decorator
        {
            public NeedToRetarget(Composite child) : base(child) { }

            protected override bool CanRun(object context)
            {
                if (Me.GotTarget && Targ.IsAlive) return false;
                if (!Me.Combat) return false;

                return true;
            }
        }

        public class Retarget : Action
        {
            protected override RunStatus Run(object context)
            {
                List<WoWUnit> hlist = (from o in ObjectManager.ObjectList
                                       where o is WoWUnit
                                       let p = o.ToUnit()
                                       where p.Distance2D < 40 && !p.Dead && (p.IsTargetingMyPartyMember || p.IsTargetingMeOrPet || p.IsTargetingMyRaidMember) && p.Attackable
                                       orderby p.HealthPercent ascending
                                       orderby p.Distance2D ascending
                                       select p).ToList();

                // No sapped target, kill anything. 
                if (hlist.Count > 0) hlist[0].Target();

                return RunStatus.Failure;
            }
        }
        #endregion

        #region Move To
        public class NeedToMoveTo : Decorator
        {
            public NeedToMoveTo(Composite child) : base(child) { }

            protected override bool CanRun(object context)
            {
                //if (GetLazyRaiderSettings().Contains("always")) return false;
                if (!Me.GotTarget) return false;
                if (Me.IsCasting) return false;

                //double distance = ;
               // if (PriestSettings.PartyHealWhen.Contains("dedicated") && Me.IsInInstance) distance = 28;

                return Target.IsDistanceMoreThan(LowestLevelRangeSpell().MaxRange);
            }
        }

        public class MoveTo : Action
        {
            protected override RunStatus Run(object context)
            {
                if (Target.IsDistanceMoreThan(LowestLevelRangeSpell().MaxRange - 10))
                    Navigator.MoveTo(Targ.Location);

                return RunStatus.Success;
            }
        }
        #endregion

        

        #region handlers

        public static void TalentPointEventHander(object sender, LuaEventArgs args)
        {
            ClassHelpers.Priest.ClassSpec = (ClassHelpers.Priest.ClassType)Talents.Spec;
            Utils.Log(string.Format("You are a level {0} {1} {2}", Me.Level, ClassHelpers.Priest.ClassSpec, Me.Class));
        }

        public static void PlayerDeadEventHandler(object sender, LuaEventArgs args)
        {
            Utils.Log("************************* PLAY-YA DE-EDD");

            foreach (object arg in args.Args)
            {
                if (!(arg is String)) continue;
                var s = (string)arg;
                Utils.Log("*********************[PLAYED DEAD EVENT FIRED]**************************");
                Utils.Log(s);
                Utils.Log("*********************[PLAYED DEAD EVENT ENDED]**************************");

            }
        }

        #endregion

        #region HB Start Up and End
        public static void BotEvents_OnBotStarted(EventArgs args)
        {
            // Finds the spec of your class: 0,1,2,3 and uses an enum to return something more logical
            ClassHelpers.Priest.ClassSpec = (ClassHelpers.Priest.ClassType)Talents.Spec;
            Utils.Log(string.Format("You are a level {0} {1} {2}", Me.Level, ClassHelpers.Priest.ClassSpec, Me.Class));

            // Do important stuff on LUA events
            Lua.Events.AttachEvent("COMBAT_LOG_EVENT", EventHandlers.CombatLogEventHander);
            Lua.Events.AttachEvent("CHARACTER_POINTS_CHANGED", TalentPointEventHander);
            Lua.Events.AttachEvent("PLAYER_TALENT_UPDATE", TalentPointEventHander);
            Lua.Events.AttachEvent("ACTIVE_TALENT_GROUP_CHANGED", TalentPointEventHander);
            //Lua.Events.AttachEvent("PARTY_MEMBER_DISABLE", PlayerDeadEventHandler);


            Timers.Add("Pulse");          // Only do certain things in the Pulse check every 1 second
            Timers.Add("DistanceCheck");
            Timers.Add("FaceTarget");
            Timers.Add("EnvironmentSettings");
            Timers.Add("MassDispel");
            Timers.Add("ArcSmiteCombat");
            Timers.Add("HealingSpells"); // HB has an issue where it does not update health fast enough. This is a workaround to prevent heal spamming
            Timers.Add("SWPain");           // So we don't spam SWP on immune targets
            Timers.Add("VampiricTouch");    // VT is being cast more than it should. I don't think HB is seing this debuff properly
            Timers.Add("MapSpam");

            // Environmental PriestSettings
            string environment = Utils.IsBattleground ? "PVP" : "PVE";
            environment = ObjectManager.Me.IsInInstance ? "Instance" : environment;
            ConfigSettings.CurrentEnvironment = environment;
            //Utils.Log(string.Format("*** Loading {0} settings.", environment),Utils.Colour("Red"));

            AkatoshCore.Instance.CurrentSpec = Talents.Spec;

            AkatoshCore.Instance.LoadSettings(false);

        }

        

/*
        void BotEvents_OnBotStopped(EventArgs args)
        {

        }
*/
        #endregion
    }

    class SpellBlacklist
    {
        static readonly Dictionary<uint, BlacklistTime> SpellBlacklistDict = new Dictionary<uint, BlacklistTime>();
        private SpellBlacklist()
        {
        }

        class BlacklistTime
        {
            public BlacklistTime(DateTime time, TimeSpan span)
            {
                TimeStamp = time;
                Duration = span;
            }
            public DateTime TimeStamp { get; private set; }
            public TimeSpan Duration { get; private set; }
        }

        static public bool Contains(uint spellID)
        {
            RemoveIfExpired(spellID);
            return SpellBlacklistDict.ContainsKey(spellID);
        }

        static public void Add(uint spellID, TimeSpan duration)
        {
            SpellBlacklistDict[spellID] = new BlacklistTime(DateTime.Now, duration);
        }

        static void RemoveIfExpired(uint spellID)
        {
            if (SpellBlacklistDict.ContainsKey(spellID) &&
                SpellBlacklistDict[spellID].TimeStamp + SpellBlacklistDict[spellID].Duration <= DateTime.Now)
            {
                SpellBlacklistDict.Remove(spellID);
            }
        }
    }
}